﻿using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace Sustainalytics.DataLayer.EsgRatings
{


    public class RPIndicatorsRepository<T> : RepositoryCollection<T>, IRPIndicatorsRepository<T>
        where T : IEntity
    {
        public RPIndicatorsRepository(string mongoDbUrl, string collectionName = null)
            : base(mongoDbUrl, collectionName)
        {
        }

        public IEnumerable<TK> GetDistinctByFields<TK>(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] properties) where TK : class
        {
            return DistinctByFields<TK>(properties, predicate);
        }

        private IEnumerable<TK> DistinctByFields<TK>(IEnumerable<Expression<Func<T, object>>> properties, Expression<Func<T, bool>> predicate = null) where TK : class
        {
            var pipelineStages = new List<BsonDocument>();

            if (predicate != null)
            {
                var query = Builders<T>.Filter.Where(predicate).Render(Collection().DocumentSerializer, Collection().Settings.SerializerRegistry);

                pipelineStages.Add(new BsonDocument
                {
                    { "$match", query }
                });
            }

            var grouping = new BsonDocument
                {
                    {
                        "$group", new BsonDocument
                        {
                            {
                                "_id",new BsonDocument()
                                {
                                     properties.Select(x => new BsonElement(x.GetPropertyName(), "$" + x.GetPropertyName())),
                                }
                            }
                        }
                    }
                };
            pipelineStages.Add(grouping);

            var projection = new BsonDocument
                {
                    {
                        "$project", new BsonDocument
                        {
                            {"_id", BsonValue.Create(0)}
                        }.AddRange(  properties.Select(x => new BsonElement(x.GetPropertyName(), "$_id." + x.GetPropertyName())))
                    }
                };

            pipelineStages.Add(projection);

            var resultObject = Collection().Aggregate(PipelineDefinition<T, BsonDocument>.Create(pipelineStages)).ToList();
            return resultObject.Select(x => BsonSerializer.Deserialize<TK>(x));
        }
    }
}
